#include <stdio.h>
#include "gmp.h"
#include "time.h"

main(){
        mpz_t e, d, n, p, q, fi, k, temp, x, y, infinity, tmp_p, tmp_q;
        mpz_t M, C, test, now;
        char input_str[1000];
        char s_infinity[1000];
        int i;
        gmp_randstate_t state;
       
        mpz_init(e);
        mpz_init(d);
        mpz_init(n);
        mpz_init(p);
        mpz_init(q);
        mpz_init(fi);
        mpz_init(k);
        mpz_init(tmp_p);
        mpz_init(tmp_q);
        mpz_init(infinity);
        mpz_init(M);
        mpz_init(C);
        mpz_init(test);
        mpz_init(now);
        gmp_randinit_default(state);
        mpz_set_ui(now, time(NULL));
        i = 0;
        gmp_randinit_mt(state);




        // FILL INFINTY (1^999)
        sprintf(s_infinity, "1%06d", 0);
        mpz_set_str(infinity, s_infinity, 10);
        gmp_randseed(state, now);

        gmp_printf("Infinity: %Zd\n", infinity);
       

        // P
        mpz_set_ui(now, time(NULL));
        gmp_randseed(state, now);
        mpz_urandomm(p, state, infinity);
        mpz_nextprime(p,p);
        gmp_printf("p: %Zd\n", p);
       

        // Q
        mpz_set_ui(now, time(NULL));
        mpz_mul(now, now, p);
        gmp_randseed(state, now);
        mpz_urandomm(q, state, infinity);
        mpz_nextprime(q,q);
        gmp_printf("q: %Zd\n", q);

        // N
        mpz_mul(n, p, q);
        gmp_printf("n: %Zd\n", n);

        // FI
        mpz_sub_ui(tmp_p, p, 1);
        mpz_sub_ui(tmp_q, q, 1);
        mpz_mul(fi, tmp_p, tmp_q);
        gmp_printf("fi: %Zd * %Zd = %Zd\n", tmp_p,tmp_q, fi);

       

        // D
        mpz_set_ui(k, 1);
        mpz_set_ui(p, 0);
        mpz_set_ui(d, 0);
        mpz_set_ui(q, 0);

        while(1==1){
                // E
                mpz_set_ui(tmp_q, 1);
                mpz_set_ui(now, time(NULL));
                gmp_randseed(state, now);
                mpz_urandomm(e, state, fi);
                mpz_gcd(tmp_p, e, fi);
                while(mpz_cmp(tmp_q, tmp_p) != 0){
                        if(mpz_cmp(e, fi) > 0){
                                mpz_set_ui(now, time(NULL));
                                gmp_randseed(state, now);
                                mpz_urandomm(e, state, fi);
                        }
                        mpz_add_ui(e, e, 1);
                        mpz_gcd(tmp_p, e, fi);
                        //gmp_printf("\n\ngdc(%Zd, %Zd) = %Zd\n\n\n", e, fi, tmp_p);
                }
                gmp_printf("e: %Zd\n", e);

                while(mpz_cmp(k, e) < 0){
                        mpz_mul(tmp_p, k, fi);
                        //gmp_printf("1 + %Zd * %Zd =", tmp_p, k, fi);
                        mpz_add_ui(tmp_p, tmp_p, 1);
                        //gmp_printf(" %Zd\n", tmp_p);
                       
                        mpz_mod(tmp_q, tmp_p, e);

                        if(mpz_cmp(tmp_q, p) == 0){
                                mpz_divexact(d, tmp_p, e);
                                break;
                        }
                        mpz_add_ui(k, k, 1);

                }
                if(mpz_cmp(d, p) != 0){
                        break;
                }
                mpz_set_ui(k, 0);

        }

        gmp_printf("k: %Zd\n", k);
        gmp_printf("d: %Zd\n", d);
       

        // TEST
        //mpz_cmp(mpz_mul())
        mpz_mul(p, e, d);
        mpz_mod(q, p, fi);
        mpz_set_ui(tmp_q, 1);
        if(mpz_cmp(q, tmp_q) == 0){
                // PRINT KEYS
                gmp_printf("\nKU = {%Zd,%Zd}\n",e,n);
                gmp_printf("KR = {%Zd,%Zd}\n",d,n);
        } else {
                gmp_printf("\nKU = {%Zd,%Zd}\n",e,n);
                gmp_printf("KR = {%Zd,%Zd}\n",d,n);

                printf("\n! ! ! E R R O ! ! !\n");
        }

        mpz_clear(e);
        mpz_clear(d);
        mpz_clear(n);
        mpz_clear(p);
        mpz_clear(q);
        mpz_clear(fi);
        mpz_clear(k);
        mpz_clear(tmp_p);
        mpz_clear(tmp_q);
        mpz_clear(infinity);
        mpz_clear(M);
        mpz_clear(C);
        mpz_clear(test);
        gmp_randclear (state);
        mpz_clear(now);

        return 0;

}
